/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

/*! @file OSGi/LifeCycle.inl
    @brief Class OSGi::LifeCycle inline methods.
    @author @ref Guillaume_Terrissol
    @date 25th February 2010 - 11th April 2014
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

namespace OSGi
{
//------------------------------------------------------------------------------
//                                Generic Change
//------------------------------------------------------------------------------

    /*! @brief Change for any bundle class.
        @version 0.5

        Because methods of classes inherited from Bundle may be used, Change
        is abstract : this class is parameterized by the type of bundle of the
        method to call to try the state change.
        @tparam TBndl Type of handled bundle (should inherit Bundle)
     */
    template<class TBndl>
    class LifeCycle::GenericChange : public Change
    {
    public:
        //! @name Type
        //@{
        using Function = std::function<bool (TBndl*, ContextPtr)>;              //!< Callback.
        //@}
        //! @name Constructor & destructor
        //@{
                    GenericChange(bool (TBndl::*pFunc)(ContextPtr),
                                  std::string   pToState,
                                  ERecurse      pRecursion);                    //!< Constructor.
virtual             ~GenericChange();                                           //!< Destructor.
        //@}

    private:

virtual bool        call(Bundle* pBundle, ContextPtr pContext) const override;  //!< Change [attempt].
        Function    mFunc;                                                      //!< Callback.
    };


//------------------------------------------------------------------------------
//                           Constructor & Destructor
//------------------------------------------------------------------------------

    /*! @param pFunc      Bundle method to call in order to try to change state
        @param pToState   Target state
        @param pRecursion How to recurse the change to children bundles
     */
    template<class TBndl>
    LifeCycle::GenericChange<TBndl>::GenericChange(
            bool (TBndl::*pFunc)(ContextPtr),
            std::string   pToState,
            ERecurse      pRecursion)
        : Change(pToState, pRecursion)
        , mFunc(pFunc)
    { }


    /*! Defaulted.
     */
    template<class TBndl>
    LifeCycle::GenericChange<TBndl>::~GenericChange() = default;


//------------------------------------------------------------------------------
//                               Change Management
//------------------------------------------------------------------------------

    /*! @copydetails LifeCycle::Change::operator()(Bundle*, ContextPtr) const
        @note The method may also return @b false if @p pBundle wasn't the right type
     */
    template<class TBndl>
    bool LifeCycle::GenericChange<TBndl>::call(Bundle* pBundle, ContextPtr pContext) const
    {
        if (TBndl* lBundle = dynamic_cast<TBndl*>(pBundle))
        {
            return mFunc(lBundle, pContext);
        }
        else
        {
            return false;
        }
    }


    /*! Records a state change for a kind of bundle.
        @param pName      Action name associated to the change
        @param pFrom      State from which to change
        @param pTo        Target state
        @param pFunc      Bundle methode to call in order to try to change state
     */
    template<class TBndl>
    void LifeCycle::declareChange(
        std::string   pName,
        std::string   pFrom,
        std::string   pTo,
        bool (TBndl::*pFunc)(ContextPtr))
    {
        
        declareChange(pName, pFrom, pTo, pFunc, ERecurse::eNo);
    }


    /*! Records a state change for a kind of bundle.
        @param pName      Action name associated to the change
        @param pFrom      State from which to change
        @param pTo        Target state
        @param pFunc      Bundle methode to call in order to try to change state
        @param pRecursion How to recurse the change to children bundles
     */
    template<class TBndl>
    void LifeCycle::declareChange(
        std::string   pName,
        std::string   pFrom,
        std::string   pTo,
        bool (TBndl::*pFunc)(ContextPtr),
        ERecurse      pRecursion)
    {
        declareChange(pName, pFrom, pTo, std::make_shared<GenericChange<TBndl>>(pFunc, pTo, pRecursion));
    }
}
